/**
 * ISWE.
 * Created on : 16/07/2009
 *
 * Copyright (c) 2009 ISWE.
 * Rafael Noronha de Almeida
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of Rafael Noronha de Almeida
 * ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Rafael.
 */
package br.com.iswe.core.dao.gae;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.orm.jpa.support.JpaDaoSupport;

import br.com.iswe.core.dao.DAO;
import br.com.iswe.core.dao.QueryDetails;
import br.com.iswe.core.exp.ISWEBadCodeException;

import com.google.appengine.api.datastore.Key;

/**
 *
 * Description: 
 *
 * @author Rafael Noronha de Almeida
 *
 * @version $Revision: 1.3 $ $Date: 2009/07/22 12:12:41 $
 *
 */
public abstract class BaseGAEDAO<ENTIDADE> extends JpaDaoSupport implements DAO<ENTIDADE> {

	
	public abstract List<ENTIDADE> getObjects(ENTIDADE entity);
	
	public abstract List<ENTIDADE> getObjects(ENTIDADE entity, Map<String, Object> filter, QueryDetails queryDetails);
	
	//// ##### getting a single object ##### ////
	
	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#getObject(java.lang.Class, java.io.Serializable)
	 */
	@Override
	public ENTIDADE getObject(Class<ENTIDADE> clazz, Serializable id) {
		ENTIDADE entidade = (ENTIDADE) getJpaTemplate().find(clazz, id);
		return entidade;
	}

	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#saveObject(java.lang.Object)
	 */
	@Override
	public void saveObject(ENTIDADE entity) {
		getJpaTemplate().persist(entity);
	}

	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#removeObject(java.lang.Object)
	 */
	@Override
	public void removeObject(ENTIDADE entity) {
		getJpaTemplate().remove(entity);
	}

	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#flush()
	 */
	@Override
	public void flush() {
		getJpaTemplate().flush();
	}
	
	//not implemented --> coisas toscas to remove
	
	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#evict(java.lang.Object)
	 */
	@Override
	public void evict(ENTIDADE entidade) {
		throw new ISWEBadCodeException("evid not supported by the current DAO configuration !!!", null);
	}

	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#clearSessionCache()
	 */
	@Override
	public void clearSessionCache() {
		throw new ISWEBadCodeException("clearSessionCache not supported by the current DAO configuration !!!", null);
	}
	
	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#evictAll(java.lang.Class)
	 */
	@Override
	public void evictAll(Class<ENTIDADE> entityClass) {
		throw new ISWEBadCodeException("evictAll not supported by the current DAO configuration !!!", null);
	}	
	
	
	@SuppressWarnings("unchecked")
	public void addRestrictions(final QueryBuilder queryBuilder, final Map filter){
		//Sem implementacao  por default
	}
	
	protected void addOrder(final QueryBuilder queryBuilder, QueryDetails queryDetails) {
		List<String> orderList = queryDetails.getOrderList();
		for(String item : orderList){
			addOrder(queryBuilder, item);
		}
	}
	
	protected void addOrder(final QueryBuilder queryBuilder, String item) {
    	String direcao  = item.substring(0,1);
		String property = item.substring(2);
		if("a".equals( direcao )){
			queryBuilder.addAscOrder(property);
		}
		else {
			queryBuilder.addDescOrder(property);
		}
	}
	
	enum QueryBuilderOperationType{
		EQUAL,
		LIKE,
		IN
	}
	
	enum QueryBuilderOrder{
		ASC,
		DESC
	}
	
	public class QueryBuilder {
		private EntityManager em;
		private String entityName;
		private List<PropertyValuePair> properties;
		private List<OrderInfo> orderInfo;
		
		/**
		 * @param em 
		 * @param string
		 */
		public QueryBuilder(String entityName, EntityManager em) {
			this.entityName = entityName;
			this.em = em;
			properties = new ArrayList<PropertyValuePair>();
		}

		/* (non-Javadoc)
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
			
			StringBuffer buffer = new StringBuffer(" select obj from ");
			buffer.append(entityName);
			buffer.append(" obj");
			for (int i = 0; i < properties.size(); i++) {
				PropertyValuePair item = properties.get(i);
				
				if(i == 0){
					buffer.append(" where");
				}
				
				buffer.append(" (");
				
				buffer.append(item.property);
				//operator
				if(item.operationType == QueryBuilderOperationType.EQUAL){
					buffer.append(" = ");
				}
				if(item.operationType == QueryBuilderOperationType.LIKE){
					buffer.append(" like '%'");
				}
				if(item.operationType == QueryBuilderOperationType.IN){
					buffer.append(" IN (");
				}
				//binding variable
				buffer.append(":");
				buffer.append(item.bindName);
				if(item.operationType == QueryBuilderOperationType.LIKE){
					buffer.append("'%' ");
				}
				if(item.operationType == QueryBuilderOperationType.IN){
					buffer.append(") ");
				}
				
				buffer.append(") ");
				
				if((i + 1) < properties.size()){
					buffer.append("AND ");
				}
			}
			
			return buffer.toString();
		}
		
		/**
		 * @param em
		 * @return
		 */
		public Query createQuery() {
			//create the query object
			Query query = em.createQuery(this.toString());
			
			for (int i = 0; i < properties.size(); i++) {
				PropertyValuePair item = properties.get(i);
				//setup the bind values
				query.setParameter(item.bindName, item.value);
			}
			
			return query;
		}
		
		/**
		 * @param property
		 */
		public void addDescOrder(String property) {
			orderInfo.add(new OrderInfo(property, QueryBuilderOrder.ASC));
		}

		/**
		 * @param property
		 */
		public void addAscOrder(String property) {
			orderInfo.add(new OrderInfo(property, QueryBuilderOrder.DESC));
		}

		/**
		 * @param property
		 * @param value
		 */
		public void addEq(String property, Object value) {
			properties.add(new PropertyValuePair(property, value, QueryBuilderOperationType.EQUAL));
		}
		
		/**
		 * @param string
		 * @param keyList
		 */
		public void addKeyList(String property, List<Key> keyList) {
			properties.add(new PropertyValuePair(property, keyList, QueryBuilderOperationType.IN));
		}

		/**
		 * @param string
		 * @param name
		 */
		public void addLike(String property, Object value) {
			properties.add(new PropertyValuePair(property, value, QueryBuilderOperationType.LIKE));
		}
		
		class OrderInfo{
			String property;
			QueryBuilderOrder direction;
			/**
			 * @param property2
			 * @param asc
			 */
			public OrderInfo(String property, QueryBuilderOrder direction) {
				this.property = property;
				this.direction = direction;
			}
		}
		
		class PropertyValuePair{
			public PropertyValuePair(String property, Object value, QueryBuilderOperationType operationType) {
				this.property = property;
				this.value = value;
				this.operationType = operationType;
				
				//TODO: implement the logic for creating the bind name
				this.bindName = property;
			}
			String property;
			String bindName;
			Object value;
			QueryBuilderOperationType operationType;
		}

		

		
	}
	
	//implemented by the specifc DAO Abs Class
	
	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#getObjects(java.lang.Object)
	 
	@SuppressWarnings("unchecked")
	@Override
	public List<ENTIDADE> getObjects(ENTIDADE o) {
		List<ENTIDADE> result = null;
		
		String queryString = "";
		
		result = getJpaTemplate().find(queryString);
		
		return result;
	}

	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#getObjects(java.lang.Object, java.util.Map, br.com.iswe.core.dao.hibernate.QueryDetails)
	 
	@Override
	public List<ENTIDADE> getObjects(ENTIDADE bean, Map<String, Object> filter, QueryDetails pageInfo) {
		return null;
	}
	

	/* (non-Javadoc)
	 * @see br.com.iswe.core.dao.DAO#getObjects(java.lang.Class)
	 
	@Override
	public List<ENTIDADE> getObjects(Class<ENTIDADE> clazz) {
		// TODO Auto-generated method stub
		return null;
	}
	*/
}
